# @Time : 2024-12-14 17:34
# @Author : Fioman 
# @Phone : 13149920693
"""
设备状态监听类,多线程来实现
"""
import threading
from datetime import datetime

import gevent

import app_config
from app.device_state import DeviceState
from common.datetime_helper import get_now_str
from common.produce_logger import debug_produce
from db_tool.db_config import TableName
from db_tool.db_handler import DbHandler
from drivers.line_camera import LineCamera
from eap_control import eap_request
from eap_control.data_model import AlarmCode, ReportType
from eap_control.eap_config import EapConfig
from personal_tool.data_result import DataResult
from personal_tool.enum_data import EqpStatus
from web_tool.send_to_web_handler import update_device_state_in_web, show_device_lock_in_web

db = DbHandler()
deviceState = DeviceState()
ec = EapConfig()


class StateListener:
    """
    设备全局状态监听类
    """
    _lock = threading.Lock()
    _instance = None

    def __new__(cls, *args, **kwargs):
        if cls._instance is None:
            with cls._lock:
                cls._instance = super().__new__(cls)
        return cls._instance

    def __init__(self, cutPlc, leftCam, rightCam):
        if not hasattr(StateListener, '_initFlag'):
            self._initFlag = True
            self.cutPlc = cutPlc
            self.leftCam: LineCamera = leftCam
            self.rightCam: LineCamera = rightCam
            debug_produce("in StateListener.__init__() 调用,进行初始化")

    def lock_device_from_web(self, activateCode):
        """
        从web端锁机
        :param activateCode:
        :return:
        """
        if activateCode != deviceState.activateCode:
            return DataResult(info="锁机密码错误")
        db.update_data(TableName.workingInfos, {"key": "1"},
                       {"lockDateTime": get_now_str()})
        self.lock_device()
        return DataResult(state=True)

    @staticmethod
    def unlock_device_from_web(activateCode, lockDateTime) -> DataResult:
        """
        解锁设备
        :return:
        """
        # 如果激活码不对就提示激活码错误
        res = DataResult()
        if activateCode != deviceState.activateCode:
            res.state = False
            res.info = "激活码错误"
            return res
        # 如果锁定时间小于当前时间就提示锁定时间错误
        if datetime.strptime(lockDateTime, "%Y-%m-%d %H:%M:%S") < datetime.now():
            res.state = False
            res.info = "锁定时间错误,锁定时间应该晚于当前时间"
            return res
        app_config.deviceLock = False

        # 将当前的锁定时间更新到数据库
        newData = {
            "lockDateTime": lockDateTime,
        }
        db.update_data(TableName.workingInfos, {"key": "1"}, newData)
        deviceState.lockDateTime = lockDateTime
        debug_produce("设备解锁成功,下次锁定时间为: {lockDateTime}")
        res.state = True
        return res

    @staticmethod
    def lock_device():
        """
        锁定设备
        """
        if not app_config.deviceLock:
            debug_produce("当前时间超过了下次锁定时间,锁定设备")
            app_config.deviceLock = True
            show_device_lock_in_web()

    def camera_groups_task(self):
        debug_produce("相机组监听任务开启")
        leftCamConnected = True
        rightCamConnected = True
        while app_config.isMainAlive:

            leftCamIsOnline = self.leftCam.is_device_online()
            rightCamIsOnline = self.rightCam.is_device_online()
            if not leftCamIsOnline:
                ret = self.reconnect_left_cam()
                if ret != 0:
                    if leftCamConnected:
                        debug_produce(str(ret))
                    leftCamConnected = False
                else:
                    leftCamIsOnline = True
                    leftCamConnected = True
                    debug_produce("上料左相机连接成功!")

            if not rightCamIsOnline:
                ret = self.reconnect_right_cam()
                if ret != 0:
                    if rightCamConnected:
                        debug_produce(str(ret))
                    rightCamConnected = False
                else:
                    rightCamIsOnline = True
                    rightCamConnected = True
                    debug_produce("上料右相机连接成功!")

            deviceState.cameraGroupState = [leftCamIsOnline, rightCamIsOnline]
            gevent.sleep(2)
            # 检测当前时间是否超过了下次锁定时间
            if (deviceState.lockDateTime and datetime.now() >
                    datetime.strptime(deviceState.lockDateTime, "%Y-%m-%d %H:%M:%S")):
                # 重新锁定设备
                self.lock_device()

        debug_produce("相机组监听任务退出")

    def reconnect_left_cam(self):
        """
        连接左相机
        :return:
        """
        try:
            ret = self.leftCam.re_connect_line_camera()
            if ret != 0:
                self.leftCam.errorMsg = "上料左相机连接失败,原因:{}".format(ret)
                return self.leftCam.errorMsg
            else:
                # 设置相机的曝光
                self.leftCam.start_grabbing()  # 打开相机
                return 0
        except Exception as e:
            print("in reconnect_left_cam() error : {}".format(str(e)))
            return "上料左相机连接失败,失败原因:{}".format(str(e))

    def reconnect_right_cam(self):
        """
        重新连接右相机
        :return: 成功返回0,失败返回失败原因
        """
        try:
            ret = self.rightCam.re_connect_line_camera()
            if ret != 0:
                self.rightCam.errorMsg = "上料右相机连接失败,原因:{}".format(ret)
                return self.rightCam.errorMsg
            else:
                self.rightCam.start_grabbing()  # 打开相机
                return 0
        except Exception as e:
            print("in reconnect_right_cam() error : {}".format(str(e)))
            return "上料右相机连接失败,失败原因:{}".format(str(e))

    def cut_plc_task(self):
        debugPre = "分板机:"
        debug_produce(f"{debugPre} 监听任务开启")
        connectFlag = False
        connetAlramReport = False  # 是否上报了连接异常报警
        while app_config.isMainAlive:
            if not deviceState.cutPlcState.isOnline:
                connectRes = self.cutPlc.connect()
                if connectRes.state:
                    debug_produce(f"{debugPre} 连接成功!")
                    if connetAlramReport and ec.eapOnline:
                        alarmType = "S"
                        reportType = ReportType.Reset.value
                        awCode = AlarmCode.PLC通信异常.value
                        awText = AlarmCode.PLC通信异常.name
                        eap_request.request_alarm_warning_data_report(alarm_type=alarmType,
                                                                      report_type=reportType, aw_code=awCode, aw_text=awText)
                        connetAlramReport = False
                    connectFlag = True
                else:
                    errorInfo = f"{debugPre} 连接失败,请查看网络是否插好"
                    if not connectFlag and ec.eapOnline:
                        gevent.sleep(1)
                        alarmType = "S"
                        reportType = ReportType.Happen.value
                        awCode = AlarmCode.PLC通信异常.value
                        awText = AlarmCode.PLC通信异常.name
                        eap_request.request_alarm_warning_data_report(alarm_type=alarmType,
                                                                      report_type=reportType, aw_code=awCode, aw_text=awText)
                        connetAlramReport = True
                        db.save_alarm_record(errorInfo)
                        debug_produce(errorInfo)  # 只显示一条日志即可,不然会一直显示
                        connectFlag = True
                deviceState.cutPlcState.isOnline = connectRes.state
            else:
                res: DataResult = self.cutPlc.is_run_state()
                if not res.state:
                    deviceState.cutPlcState.isOnline = False
                    connectFlag = False
                if res.data:
                    if not deviceState.cutPlcState.isRun:
                        debug_produce(f"{debugPre} 检测到进入运行状态")
                    deviceState.cutPlcState.isRun = True
                else:
                    if deviceState.cutPlcState.isRun:
                        debug_produce(f"{debugPre} 检测到进入停止状态")
                        ec.update_eap_status(EqpStatus.Stop)
                    deviceState.cutPlcState.isRun = False

                res = self.cutPlc.is_rs_state()
                if not res.state:
                    deviceState.cutPlcState.isOnline = False
                    connectFlag = False
                if res.data:
                    if not deviceState.cutPlcState.isRs:
                        errorInfo = f"{debugPre} 检测到进入急停状态"
                        debug_produce(errorInfo)
                        db.save_alarm_record(errorInfo)
                        ec.update_eap_status(EqpStatus.Alarm)
                        if ec.eapOnline:
                            alarmType = "S"
                            reportType = ReportType.Happen.value
                            awCode = AlarmCode.设备急停.value
                            awText = AlarmCode.设备急停.name
                            eap_request.request_alarm_warning_data_report(alarm_type=alarmType,
                                                                          report_type=reportType, aw_code=awCode, aw_text=awText)
                    deviceState.cutPlcState.isRs = True
                else:
                    if deviceState.cutPlcState.isRs:
                        errorInfo = f"{debugPre} 检测到急停状态去除"
                        db.save_alarm_record(errorInfo)
                        debug_produce(errorInfo)
                        ec.update_eap_status(EqpStatus.Stop)
                        if ec.eapOnline:
                            alarmType = "R"
                            reportType = ReportType.Reset.value
                            awCode = AlarmCode.设备急停.value
                            awText = AlarmCode.设备急停.name
                            eap_request.request_alarm_warning_data_report(alarm_type=alarmType,
                                                                          report_type=reportType, aw_code=awCode, aw_text=awText)
                    deviceState.cutPlcState.isRs = False
            gevent.sleep(2)
            update_device_state_in_web(deviceState)

        debug_produce(f"{debugPre} 监听任务退出")

    def start_listeners(self):
        """启动所有监听任务"""

        self.cameraGroupsListener = threading.Thread(target=self.camera_groups_task, daemon=True)
        self.cutPlcListener = threading.Thread(target=self.cut_plc_task, daemon=True)

        self.cameraGroupsListener.start()
        self.cutPlcListener.start()

        debug_produce("所有监听任务启动完成")


if __name__ == '__main__':
    pass
